/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.wider.foundation.channel;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.HttpURLConnection;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

/**
 * Utility class for user to construct a HTTP client which has the ability of performing the GET and POST operation as well as the reponse handling for the corresponding return path
 * To use this class, the JavaMail API (mailapi.jar) MUST be installed in your Java CLASSPATH accordingly.
 * @author William
 */
public class HttpChannel {
    private String  base_url_string;
    private Logger  logger;
    
    /**
     * Public interface for user to mount the implementation of the HTTP Response Handler
     * @param resp_code the returned HTTP Response Code
     * @param resp_mesg the returned HTTP Response Message
     * @param stream_in the reference of the InputStream for the returned content
     */
    public  static interface HttpResponseHandlerImpl{
        public void handle(int resp_code, String resp_mesg, InputStream stream_in) throws IOException;
    }

    /**
     * Private inner class which defines the Runnable (Thread) context
     */
     private class RunnableRequest implements Runnable{
        HttpURLConnection   l_httpconn;
        HttpResponseHandlerImpl l_httphndlr;
        int     ret_code;
        String  ret_mesg;

        public RunnableRequest(HttpURLConnection httpconn, HttpResponseHandlerImpl handler){
            l_httpconn = httpconn;
            l_httphndlr = handler;
        }

        public void run() {
            try {
                ret_code = l_httpconn.getResponseCode();
                ret_mesg = l_httpconn.getResponseMessage();
                l_httphndlr.handle(ret_code, ret_mesg, l_httpconn.getInputStream());
            } catch (IOException ex) {
                try {
                    l_httphndlr.handle(ret_code, ret_mesg, l_httpconn.getInputStream());
                    logger.log(Level.FINE, "HTTP Request Thread caught I/O Exception from the HTTP connection.", ex);
                } catch (IOException ex1) {
                   logger.log(Level.SEVERE,"HTTP Request Thread caught I/O Exception from the user handler.", ex1);
                }
            }
        }
     }

    /**
     * Class constructor for creating the HttpChannel instance
     * @param url an absolute URL giving the base location of the web service.
     * @throws MalformedURLException if the input url is not correct.
     * @throws IOException if the service identified by the url doesn't exist.
     * 
     */
    public HttpChannel(String url) throws MalformedURLException, IOException{
        try {
            logger = Logger.getLogger(this.getClass().getName());
            
            URL http_url = new URL(url);
            HttpURLConnection http_conn = (HttpURLConnection) http_url.openConnection();

            //Try access to foresee if this url is applicable. Exception will occur if access failed.
            http_conn.getResponseCode();

            //Save the url as the base url for this channel object
            base_url_string = url;
            
        }catch (MalformedURLException ex) {
            logger.log(Level.INFO, "MalformedURLException occurs for the URL: " + url, ex);
            throw ex;
        }catch (IOException ex) {
            logger.log(Level.INFO, "IOException occurs for the URL: " + url, ex);
            throw ex;
        }
    }

    /**
     * Method for performing HTTP GET operation.
     * It is based on the channel's base url with key-value pairs as paramters in addition.
     * This implementation is non-blocking design, so user must feed in a callback handler
     * for receving the GET response.
     * 
     * @param params    HTTP GET paramters, passing through using a Map of &lt;Key: String, Value any&gt;
     * @param handler   Implementation of interface HttpResponseHandlerImpl. It acts as the receiving callback
     */

    public void get(Map<Object, Object> params, HttpResponseHandlerImpl handler) throws UnsupportedEncodingException, MalformedURLException, IOException{

        //Retrieve the parameters and construct the params string
        Iterator paramsit = params.entrySet().iterator();
        StringBuilder rqst_params = new StringBuilder();

        while(paramsit.hasNext()){
                Map.Entry param = (Map.Entry) paramsit.next();
                StringBuilder param_string = new StringBuilder();
                param_string.append(param.getKey()).append("=").append(URLEncoder.encode(param.getValue().toString(), "utf8"));

                //Append this key-value pair into the rqst_params
                rqst_params.append("&").append(param_string);
        }

        //Replace the leading '&' with '?'
        rqst_params.replace(0, 1, "?");
        
        try{
            //Prepare the HttpUrlConnection
            URL httpurl = new URL(base_url_string + rqst_params);
            HttpURLConnection httpconn = (HttpURLConnection) httpurl.openConnection();

            //Set to use the HTTP GET method as well as Input & Output processing
            httpconn.setRequestMethod("GET");
            httpconn.setDoOutput(true);
            httpconn.setDoInput(true);
            httpconn.setRequestProperty("Charset", "UTF-8");
            //httpconn.set

            //Instanciate the worker thread using the RunnableGet
            Thread worker = new Thread(new RunnableRequest(httpconn, handler));

            //Invoke the worker thread
            worker.start();
 
        }catch (MalformedURLException ex) {
            logger.log(Level.WARNING, "MalformedURLException occurs at the constructed URL.", ex);
            throw ex;
        }catch (IOException ex) {
            logger.log(Level.WARNING, "IOException occurs while starting the HTTP GET transaction.", ex);
            throw ex;
        }
    }

    /**
     * Method for performing HTTP POST operation.
     * Similar to the GET method, all the connection is based on the channel's base url.
     * All parameters / data will be carried in the payload in a MIME type format.
     * It currently supports only the HTML major MIME type - multipart/form-data.
     * This implementation is non-blocking design, so user must feed in a callback handler
     * for receving the POST response.
     *
     * planned features: multiple files / large file in multipart /
     * @param params    Parameters for the HTTP POST operation, passing through using a Map of &lt;Key: Object, Value: Object&gt;<br>
     *                      e.g. &lt;Key: File [file handler], Value: String [param name]&gt; means file uploading.<br>
     *                      e.g. &lt;Key: String [param name], Value: String [content]&gt; means plain text.
     * @param handler   Implementation of interface HttpResponseHandlerImpl. It acts as the receiving callback
     * @throws InterruptedException 
     */
    public void post(Map<Object, Object> params, HttpResponseHandlerImpl handler) throws MalformedURLException, IOException, MessagingException, InterruptedException {
        try {
            //Retrieve the parameters and construct the params string
            Iterator paramsit = params.entrySet().iterator();

            //Instanciate an MimeMultipart as a container
            MimeMultipart multipart = new MimeMultipart();
            multipart.setSubType("form-data");

            //Iterates each parameter pair
            while (paramsit.hasNext()) {
                //Get the key-value pair from the Iterator
                Map.Entry param = (Map.Entry) paramsit.next();
                Object paramkey = param.getKey();
                //System.out.println("before "+param.getValue().toString())  ;
                //System.out.println("afyer "+URLEncoder.encode(param.getValue().toString(), "utf-8"))  ;
                Object paramval =URLEncoder.encode(param.getValue().toString(), "utf8");//*/param.getValue();

                //FileOutputStream output =new FileOutputStream("D://output");
                //PrintWriter pnd;
                //("inhttpcahnnle key & val"+paramkey.toString()+" "+paramval.toString());
                //Construct the bodypart according to the type, then attach it to the Multipart
                MimeBodyPart mimebody = new MimeBodyPart();

                //If the paramkey is type of File, the paramval will be used as the key of the bodypart
                if (paramkey.getClass().equals(File.class)){
                    File file = (File) paramkey;
                    mimebody.setDisposition("file; name=" + (String)paramval);
                    mimebody.attachFile(file);
                    mimebody.setFileName(MimeUtility.encodeText(file.getName(), "utf8", "B")); //B means Base64
                    multipart.addBodyPart(mimebody);
                } 
                else{
                	if(paramval.getClass().equals(Vector.class)){
                    	mimebody.setDisposition("vector; name=" + paramkey);
                    	mimebody.setContent(paramval, "vector");
                    	multipart.addBodyPart(mimebody);
                    	System.out.println("Vector! in http");
                		
                	}
                	else{
                    //Not a file, currently supports SetText only
                    String content = paramval.toString();
                    mimebody.setDisposition("form-data; name=" + paramkey);
                    mimebody.setText(MimeUtility.encodeText(content, "utf8", "B"));  
                    multipart.addBodyPart(mimebody);
                    System.out.println("else!in http");
                	}
                }
            }

            //Prepare the HttpUrlConnection
            URL httpurl = new URL(base_url_string);
            HttpURLConnection httpconn = (HttpURLConnection) httpurl.openConnection();

            //Set to use the HTTP GET method as well as Input & Output processing
            httpconn.setRequestMethod("POST");
            httpconn.setRequestProperty("content-type", multipart.getContentType());
            httpconn.setDoOutput(true);
            httpconn.setDoInput(true);
          
            //Attach the multipart to the connection stream
           
            multipart.writeTo(httpconn.getOutputStream());
          
            
            //Instanciate the worker thread using the RunnableRequest
            Thread worker = new Thread(new RunnableRequest(httpconn, handler));

            //Invoke the worker thread
            worker.start();
            //synchronized(this){worker.wait();}
            

        }catch (MalformedURLException ex) {
            logger.log(Level.WARNING, "MalformedURLException occurs at the base URL.", ex);
            throw ex;
        }catch (IOException ex) {
            logger.log(Level.WARNING, "IOException occurs while starting the HTTP POST transaction.", ex);
            throw ex;
        }catch (MessagingException ex) {
            logger.log(Level.WARNING, "MessagingException occurs while starting the HTTP POST transaction.", ex);
            throw ex;
        }

    }

}
